﻿using ClockLib.Diagnostics;
using Microsoft.Toolkit.Uwp.Notifications;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.Background;
using Windows.Data.Json;
using Windows.Foundation;
using Windows.Storage;
using Windows.UI.Notifications;

namespace ClockLib
{
    public sealed class ClockManager
    {
        const string SuspendClockDataKey = "SuspendClockData";
        const string AvtiveClockDataKey = "AvtiveClockData";
        //const string ClockToastContentKey = "ClockToastContent";
        const string ClockBackgroundTaskName = "ClockBackgroundTask";
        object suspendListLocker = new object();
        object avtiveListLocker = new object();
        public event EventHandler<IList<Clock>> ClockArrived;
        public event EventHandler<bool> ClockListChanged;
        public static ClockManager Instance => LazyInstance.Value;
        private static readonly Lazy<ClockManager> LazyInstance = new Lazy<ClockManager>(() =>
        {
            return new ClockManager();
        }, LazyThreadSafetyMode.ExecutionAndPublication);
        ToastNotifier toastNotifier;
        private ClockManager()
        {
            toastNotifier = ToastNotificationManager.CreateToastNotifier();
            //toastNotifier.ScheduledToastNotificationShowing += ToastNotifier_ScheduledToastNotificationShowing;
            //if (!ApplicationData.Current.LocalSettings.Values.Keys.Contains(ClockToastContentKey))
            //{
            //
            //}

            RegisterBackgroundTask();

        }

        public void RegisterBackgroundTask()
        {
            if(!BackgroundTaskRegistration.AllTasks.Values.Any(task => task.Name == ClockBackgroundTaskName))
            {
                var name = ClockBackgroundTaskName;
                var task = new BackgroundTaskBuilder
                {
                    Name = name,
                };
                var trigger = new ToastNotificationHistoryChangedTrigger();
                task.SetTrigger(trigger);
                task.Register();
            }
        }

        public void HandleBackgroundTask(BackgroundActivatedEventArgs args)
        {
            var deferral = args.TaskInstance.GetDeferral();
            ActiveNextClock();
            deferral.Complete();
            return;
        }

        //private void ToastNotifier_ScheduledToastNotificationShowing(ToastNotifier sender, ScheduledToastNotificationShowingEventArgs args)
        //{
        //    if (ClockArrived == null) return;
        //    var deferral = args.GetDeferral();
        //    ToastNotificationHistory toastNotificationHistory = ToastNotificationManager.History;
        //    toastNotificationHistory.Remove(args.ScheduledToastNotification.Tag);
        //    args.Cancel = true;
        //    deferral.Complete();
        //    ActiveNextClock();
        //}
        /// <summary>
        /// To request the access for alarm.
        /// This function just can be called in UI thread.
        /// </summary>
        /// <returns>Request result</returns>
        public IAsyncOperation<AlarmAccessStatus> RequestAlarmAccessAsync()
        {
            return AlarmApplicationManager.RequestAccessAsync();
        }


        /// <summary>
        /// To request the access for baakground task.
        /// This function just can be called in UI thread.
        /// </summary>
        /// <returns>Request result</returns>
        public IAsyncOperation<BackgroundAccessStatus> RequestBackgroundTaskAccessAsync()
        {
            return BackgroundExecutionManager.RequestAccessAsync();
        }

        //public void SetNotifyToastContent(string xml)
        //{
        //    
        //    //try
        //    //{
        //        //XmlDocument content = new XmlDocument();
        //        //content.LoadXml(xml);
        //    ApplicationData.Current.LocalSettings.Values[ClockToastContentKey] = xml;
        //    //}
        //    //catch (Exception)
        //    //{
        //
        //    //    throw;
        //    //}
        //    
        //}


        private JsonArray ToJsonArray(SortedList<long, List<Clock>> clocks)
        {
            JsonArray info = new JsonArray();
            foreach (var item in clocks)
            {
                JsonObject jo = new JsonObject();
                jo.Add("key",JsonValue.CreateNumberValue(item.Key));
                JsonArray ja = new JsonArray();
                foreach (var item2 in item.Value)
                {
                    ja.Add(item2.ClockData.ToJsonObject());
                }
                jo.Add("value",ja);
                info.Add(jo);
            }
            return info;
        }

        private SortedList<long, List<Clock>> FromJsonObject(JsonArray info)
        {
            SortedList<long, List<Clock>> res = new SortedList<long, List<Clock>>();
            foreach (var item in info)
            {
                List<Clock> clocks = new List<Clock>();
                foreach (var item2 in item.GetObject()["value"].GetArray())
                {
                    ClockData clockData = new ClockData();
                    clockData.FromJsonObject(item2.GetObject());
                    Clock clock = new Clock()
                    {
                        ClockData = clockData
                    };
                    clocks.Add(clock);
                }
                res.Add((long)item.GetObject()["key"].GetNumber(),clocks);
            }
            return res;
        }

        private SortedList<long, List<Clock>> suspendClockData;

        private SortedList<long, List<Clock>> SuspendClockData
        {
            get
            {
                if(suspendClockData==null)
                {
                    JsonArray ja = null;
                    if (ApplicationData.Current.LocalSettings.Values.Keys.Contains(SuspendClockDataKey))
                    {
                        ja = JsonArray.Parse(ApplicationData.Current.LocalSettings.Values[SuspendClockDataKey].ToString());
                    }
                    else
                    {
                        ja = new JsonArray();
                    }
                    suspendClockData = FromJsonObject(ja);
                }

                return suspendClockData;
            }
            set
            {
                ApplicationData.Current.LocalSettings.Values[SuspendClockDataKey] = ToJsonArray(value).ToString();
                suspendClockData = value;
            }
        }

        private SortedList<long, List<Clock>> activeClockData;

        private SortedList<long, List<Clock>> ActiveClockData
        {
            get
            {
                if(activeClockData==null)
                {
                    JsonArray ja = null;
                    if (ApplicationData.Current.LocalSettings.Values.Keys.Contains(AvtiveClockDataKey))
                    {
                        ja = JsonArray.Parse(ApplicationData.Current.LocalSettings.Values[AvtiveClockDataKey].ToString());
                    }
                    else
                    {
                        ja = new JsonArray();
                    }
                    activeClockData = FromJsonObject(ja);
                }
                return activeClockData;
            }
            set
            {
                ApplicationData.Current.LocalSettings.Values[AvtiveClockDataKey] = ToJsonArray(value).ToString();
                activeClockData = value;
            }
        }

        public IList<Clock> Clocks
        {
            get
            {
                List<Clock> clocks = new List<Clock>();
                foreach (var item in AllClock)
                {
                    foreach (var item2 in item.Value)
                    {
                        clocks.Add(item2);
                    }
                }
                return clocks;
            }
        }
        public ClockData GetClockDataByID(long clockID)
        {
            foreach (var item in Clocks)
            {
                if(item.ClockData.Clock_id==clockID)
                {
                    return item.ClockData;
                }
            }
            return null;
        }
        private SortedList<long,List<Clock>> AllClock
        {
            get
            {
                lock (avtiveListLocker)
                {
                    lock (suspendListLocker)
                    {
                        SortedList<long, List<Clock>> res = new SortedList<long, List<Clock>>();
                        SortedList<long, List<Clock>> activeList = ActiveClockData;
                        SortedList<long, List<Clock>> suspendList = SuspendClockData;
                        foreach (var item in suspendList)
                        {
                            res.Add(item.Key, item.Value);
                        }
                        foreach (var item in activeList)
                        {
                            if (res.ContainsKey(item.Key))
                            {
                                res[item.Key].AddRange(item.Value);
                            }
                            else
                            {
                                res[item.Key] = item.Value;
                            }
                        }
                        return res;
                    }
                }
            }
        }


        private void CancelToast()
        {
            foreach (var item in toastNotifier.GetScheduledToastNotifications())
            {
                toastNotifier.RemoveFromSchedule(item);
            }
        }

        private void ScheduleToast(long timeTick,IList<Clock> clocks)
        {
            string events = "";
            foreach (var item in clocks)
            {
                if (!string.IsNullOrEmpty(item.ClockData.Clock_event))
                {
                    events += item.ClockData.Clock_event + "\r\n";
                }
            }
            if (events.Length > 2)
            {
                events = events.Remove(events.Length - 2);
            }
            var toastContent = new ToastContent()
            {
                ActivationType= ToastActivationType.Background,
                Duration = ToastDuration.Long,
                Visual = new ToastVisual()
                {

                    BindingGeneric = new ToastBindingGeneric()
                    {
                        AppLogoOverride = new ToastGenericAppLogo() { Source = "ms-appx:///ClockLib/Alarm.png" },
                        Children =
                        {

                            new AdaptiveText()
                            {
                                Text = "这只是一个简单的提醒",
                            },
                            new AdaptiveText()
                            {
                                Text = DateTimeOffset.FromUnixTimeSeconds(timeTick).LocalDateTime.ToString("MM月dd日  HH:mm "),
                            }
                        }
                    }
                },
                Scenario = ToastScenario.Alarm,
                Audio = new ToastAudio()
                {
                    Loop = true,
                    Src = new Uri("ms-appx:///ClockLib/AlarmSound.wma")
                },
            };
            toastContent.Actions = new ToastActionsCustom() { Buttons = { new ToastButton("关闭", "") {ActivationType= ToastActivationType.Background } } };
            var scheduledToast = new ScheduledToastNotification(

               toastContent.GetXml(),DateTimeOffset.FromUnixTimeSeconds(timeTick));
            scheduledToast.Tag = "Clock"+ timeTick;
            toastNotifier.AddToSchedule(scheduledToast);
        }

        public void RemoveToastFromHistory(long tick)
        {
            ToastNotificationHistory toastNotificationHistory = ToastNotificationManager.History;
            toastNotificationHistory.Remove("Clock" + tick);
        }

        //SortedList<long, List<Clock>> ClockSorted = new SortedList<long, List<Clock>>();

        public void AddClock(ClockData clockData)
        {
            if (ContainsID(clockData.Clock_id)) return;
            AddClock(new Clock() {ClockData=clockData });
        }
        private void AddClock(Clock clock)
        {
            if(clock.IsAlarted)
            {
                return;
            }
            if (ContainsID(clock.ClockData.Clock_id))
            {
                AdjustClock(clock.ClockData.Clock_id,clock.ClockData);
            }
            lock (avtiveListLocker)
            {
                lock (suspendListLocker)
                {
                    CancelToast();
                    if(ActiveClockData.Count!=0)
                    {
                        foreach (var item in ActiveClockData)
                        {
                            if(SuspendClockData.ContainsKey(item.Key))
                            {
                                SuspendClockData[item.Key].AddRange(item.Value);
                            }
                            else
                            {
                                SuspendClockData[item.Key] = item.Value;
                            }
                        }
                        ActiveClockData.Clear();

                    }
                    if (SuspendClockData.ContainsKey(clock.ClockData.Trig_time))
                    {
                        SuspendClockData[clock.ClockData.Trig_time].Add(clock);
                    }
                    else
                    {
                        SuspendClockData[clock.ClockData.Trig_time] = new List<Clock>() { clock };
                    }
                    SuspendClockData = SuspendClockData;
                    ActiveClockData = ActiveClockData;
                    ActiveNextClock();
                }
            }
        }

        public bool ContainsID(long id)
        {
            foreach (var item in AllClock)
            {
                foreach (var item2 in item.Value)
                {
                    if (id == item2.ClockData.Clock_id)
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        public void RemoveClock(long clockID)
        {
            foreach (var item in AllClock)
            {
                foreach (var item2 in item.Value)
                {
                    if (clockID == item2.ClockData.Clock_id)
                    {
                        RemoveClock(item2);
                        break;
                    }
                }
            }
        }

        private void RemoveClock(Clock clock)
        {
            lock (avtiveListLocker)
            {
                lock (suspendListLocker)
                {
                    CancelToast();
                    if (ActiveClockData.Count != 0)
                    {
                        foreach (var item in ActiveClockData)
                        {
                            if (SuspendClockData.ContainsKey(item.Key))
                            {
                                SuspendClockData[item.Key].AddRange(item.Value);
                            }
                            else
                            {
                                SuspendClockData[item.Key] = item.Value;
                            }
                        }
                        ActiveClockData.Clear();
                    }
                    if (SuspendClockData.ContainsKey(clock.ClockData.Trig_time))
                    {
                        SuspendClockData[clock.ClockData.Trig_time].Remove(clock);
                        if(SuspendClockData[clock.ClockData.Trig_time].Count==0)
                        {
                            SuspendClockData.Remove(clock.ClockData.Trig_time);
                        }
                    }
                    SuspendClockData = SuspendClockData;
                    ActiveClockData = ActiveClockData;
                    ActiveNextClock();
                }
            }

        }

        public void ActiveNextClock()
        {
            lock (avtiveListLocker)
            {
                lock (suspendListLocker)
                {
                    while (ActiveClockData.Count != 0)
                    {
                        var activeClocks = ActiveClockData.First();
                        if (activeClocks.Key > DateTimeOffset.Now.ToUnixTimeSeconds())
                        {
                            ActiveClockData = ActiveClockData;
                            SuspendClockData = SuspendClockData;
                            ClockListChanged?.Invoke(this,true);
                            return;
                        }
                        foreach (var item in activeClocks.Value)
                        {
                            if (item.ClockData.Repeat_type == RepeatType.NoRepeat)
                            {
                                continue;
                            }
                            Clock clock = new Clock() { ClockData = new ClockData(item.ClockData) };
                            while (clock.IsAlarted)
                            {
                                clock.ResetTrigTime();
                            }
                            if (SuspendClockData.ContainsKey(clock.ClockData.Trig_time))
                            {
                                SuspendClockData[clock.ClockData.Trig_time].Add(clock);
                            }
                            else
                            {
                                SuspendClockData[clock.ClockData.Trig_time] = new List<Clock>() { clock};
                            }
                        }
                        ActiveClockData.Remove(activeClocks.Key);
                        ClockArrived?.Invoke(this, activeClocks.Value);
                    }
                    CancelToast();
                    while (SuspendClockData.Count != 0)
                    {
                        var clocksActiving = SuspendClockData.First();
                        if(clocksActiving.Key < DateTimeOffset.Now.ToUnixTimeSeconds())
                        {
                            foreach (var item in clocksActiving.Value)
                            {
                                if (item.ClockData.Repeat_type == RepeatType.NoRepeat)
                                {
                                    continue;
                                }
                                Clock clock = new Clock() { ClockData = new ClockData(item.ClockData) };
                                while (clock.IsAlarted)
                                {
                                    clock.ResetTrigTime();
                                }
                                if (SuspendClockData.ContainsKey(clock.ClockData.Trig_time))
                                {
                                    SuspendClockData[clock.ClockData.Trig_time].Add(clock);
                                }
                                else
                                {
                                    SuspendClockData[clock.ClockData.Trig_time] = new List<Clock>() { clock };
                                }
                            }
                            SuspendClockData.Remove(clocksActiving.Key);
                            ClockArrived?.Invoke(this, clocksActiving.Value);
                        }
                        else
                        {
                            ActiveClockData.Add(clocksActiving.Key,clocksActiving.Value);
                            SuspendClockData.Remove(clocksActiving.Key);
                            ScheduleToast(clocksActiving.Key, clocksActiving.Value);
                            break;
                        }

                    }
                    ActiveClockData = ActiveClockData;
                    SuspendClockData = SuspendClockData;
                    ClockListChanged?.Invoke(this,true);
                }
            }

        }

        public void RemoveAll()
        {
            lock (avtiveListLocker)
            {
                lock (suspendListLocker)
                {
                    CancelToast();
                    SuspendClockData.Clear();
                    ActiveClockData.Clear();
                    SuspendClockData = SuspendClockData;
                    ActiveClockData = ActiveClockData;
                }
            }
        }

        public void AdjustClock(long clockID,ClockData clockData)
        {
            foreach (var item in AllClock)
            {
                foreach (var item2 in item.Value)
                {
                    if (clockID == item2.ClockData.Clock_id)
                    {
                        RemoveClock(item2);
                        item2.ClockData = clockData;
                        AddClock(item2);
                        break;
                    }
                }
            }
        }

    }

}
